home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / msysjour / vol06 / 04 / winstdio / winio.c < prev    next >
C/C++ Source or Header  |  1991-07-01  |  33KB  |  967 lines

  1. /*
  2. WINIO.C
  3. Stdio (e.g. printf) functionality for Windows - implementation
  4. Dave Maxey - 1991
  5. */
  6.  
  7. #include <windows.h>
  8. #include <stdlib.h>
  9. #include <stdarg.h>
  10. #include <malloc.h>
  11. #include <string.h>
  12. #include "wmhandlr.h"
  13. #include "winio.h"
  14.  
  15. /* PROTOTYPES in alphabetic order */
  16.  
  17. void            addchars(BYTE *, unsigned);
  18. void            adjust_caret(void);
  19. void            append2buffer(BYTE *, unsigned);
  20. int             chInput(void);
  21. void            compute_repaint(void);
  22. int             initialize_buffers(unsigned);
  23. int             initialize_class(HANDLE);
  24. void            initialize_state(void);
  25. int             initialize_window(HANDLE, HANDLE, int);
  26. void            make_avail(unsigned);
  27. BYTE far *      nextline(BYTE far *);
  28. BYTE far *      prevline(BYTE far *);
  29. void            set_font(void);
  30. long            winio_wmpaint(HWND, unsigned, WORD, LONG);
  31. long            winio_wmsize(HWND, unsigned, WORD, LONG);
  32. long            winio_wmdestroy(HWND, unsigned, WORD, LONG);
  33. long            winio_wmchar(HWND, unsigned, WORD, LONG);
  34. long            winio_wmkeydown(HWND, unsigned, WORD, LONG);
  35. long            winio_wmhscroll(HWND, unsigned, WORD, LONG);
  36. long            winio_wmvscroll(HWND, unsigned, WORD, LONG);
  37. long            winio_wmsetfocus(HWND, unsigned, WORD, LONG);
  38. long            winio_wmkillfocus(HWND, unsigned, WORD, LONG);
  39.  
  40. // this doesn't get declared in stdio.h if _WINDOWS is defined
  41. // although it is in the Windows libraries!
  42. int             vsprintf(char *, const char *, va_list);
  43.  
  44. #define         winio_caret_visible() \
  45.                 ((yCurrLine <= (yTopOfWin + yWinHeight)) && \
  46.                 (xCurrPos <= (xLeftOfWin + xWinWidth)) && \
  47.                 (xCurrPos >= xLeftOfWin))
  48.  
  49. #define         CHECK_INIT() if (! tWinioVisible) return FALSE
  50.                  
  51. #define         MAX_X                   127
  52. #define         TABSIZE                 8
  53. #define         TYPE_AHEAD              256
  54. #define         WINIO_DEFAULT_BUFFER    8192
  55. #define         MIN_DISCARD             256
  56. #define         CARET_WIDTH             2
  57.  
  58. // For scrolling procedures
  59. #define         USE_PARAM               10000
  60. #define         DO_NOTHING              10001
  61.  
  62. BYTE            winio_class[15] = "winio_class";
  63. BYTE            winio_icon[15] = "winio_icon";
  64. BYTE            winio_title[128] = "Console I/O";
  65. unsigned long   bufsize = WINIO_DEFAULT_BUFFER;
  66. unsigned long   kbsize = TYPE_AHEAD;
  67. unsigned        bufused, bufSOI;
  68. unsigned        curr_font = SYSTEM_FIXED_FONT;
  69. int             tWinioVisible = FALSE;
  70. int             tCaret = FALSE, tFirstTime = TRUE;
  71. int             cxChar, cyChar, cxScroll, cyScroll, cxWidth, cyHeight;
  72. int             xWinWidth, yWinHeight, xCurrPos;
  73. int             xLeftOfWin, yTopOfWin, yCurrLine;
  74. unsigned        pchKbIn, pchKbOut;
  75. static BYTE far *fpBuffer, far *fpTopOfWin, far *fpCurrLine; 
  76. static BYTE far *fpKeyboard;
  77. static HANDLE   hBuffer, hKeyboard;
  78. static HWND     hwnd;
  79. BOOL            tTerminate = TRUE,
  80.                 tPaint = TRUE;
  81. DESTROY_FUNC    destroy_func;
  82.  
  83. typedef struct {
  84.     int hSB, vSB;
  85.     } recVKtoSB;
  86.                 
  87. /* This table defines, by scroll message, what increment to try */
  88. /* and scroll horizontally. PGUP and PGDN entries are updated   */
  89. /* in the winio_wmsize function.                                */
  90. int             cScrollLR[SB_ENDSCROLL + 1] =
  91. //UP  DOWN PGUP     PGDN    POS        TRACK      TOP     BOT    ENDSCROLL
  92. { -1, +1,  -1,      +1,     USE_PARAM, USE_PARAM, -MAX_X, MAX_X, DO_NOTHING};
  93.                 
  94. /* This table defines, by scroll message, what increment to try */
  95. /* and scroll horizontally. PGUP and PGDN entries are updated   */
  96. /* in the winio_wmsize function, and the TOP & BOTTOM entries   */
  97. /* are updated by addchar function.                             */
  98. int             cScrollUD[SB_ENDSCROLL + 1] =
  99. //UP  DOWN PGUP     PGDN    POS        TRACK      TOP     BOT    ENDSCROLL
  100. { -1, +1,  -1,      +1,     USE_PARAM, USE_PARAM, -1,     +1,    DO_NOTHING};
  101.                 
  102. /* This table associates horizontal and vertical scroll         */
  103. /* messages that should be generated by the arrow and page keys */
  104. recVKtoSB       VKtoSB[VK_DOWN - VK_PRIOR + 1] =
  105. //                  VK_PRIOR                    VK_NEXT
  106.                 {   { DO_NOTHING, SB_PAGEUP },  { DO_NOTHING, SB_PAGEDOWN },
  107. //                  VK_END                      VK_HOME
  108.                     { SB_TOP, SB_BOTTOM },      { SB_TOP, SB_TOP },
  109. //                  VK_LEFT                     VK_UP
  110.                     { SB_LINEUP, DO_NOTHING },  { DO_NOTHING, SB_LINEUP },
  111. //                  VK_RIGHT                    VK_DOWN
  112.                     { SB_LINEDOWN, DO_NOTHING },{ DO_NOTHING, SB_LINEDOWN } };
  113.                 
  114. /* ===================================================================  */
  115. /* the interface functions themselves.....                              */
  116. /* ===================================================================  */
  117.  
  118. BYTE *gets(BYTE *pchTmp)
  119.     {
  120.     BYTE *pch = pchTmp;
  121.     int c;
  122.  
  123.     CHECK_INIT();
  124.     bufSOI = bufused; /* mark beginning of line to limit backspace */
  125.     do {
  126.         switch (c = fgetchar())
  127.             {
  128.             case '\b' :     if (pch > pchTmp) pch--; break;
  129.             case 0x1b :     pch = pchTmp; break;
  130.             case EOF :      bufSOI = -1; return NULL;
  131.             default :       *pch = (BYTE) c; pch++;
  132.             }
  133.         } while (c);
  134.     bufSOI = -1;
  135.     return pchTmp;
  136.     }
  137.  
  138. int printf(const BYTE *fmt, ...)
  139.     {
  140.     va_list marker;
  141.     va_start(marker, fmt);
  142.     return vprintf(fmt, marker);
  143.     }
  144.  
  145. int vprintf(const BYTE *fmt, va_list marker)
  146.     {
  147.     static BYTE s[1024];
  148.     int len;
  149.  
  150.     CHECK_INIT();
  151.     len = vsprintf(s, fmt, marker);
  152.     addchars(s,len);
  153.     return len;
  154.     }
  155.  
  156. int fgetchar(void)
  157.     {
  158.     CHECK_INIT();
  159.     return fputchar(chInput());
  160.     }
  161.  
  162. int kbhit(void)
  163.     {
  164.     CHECK_INIT();
  165.     return (pchKbIn == pchKbOut);
  166.     }
  167.  
  168. int fputchar(int c)
  169.     {
  170.     CHECK_INIT();
  171.     addchars(&((char) c), 1);
  172.     return c;
  173.     }
  174.  
  175. int puts(const BYTE *s)
  176.     {
  177.     BYTE c = '\n';
  178.     CHECK_INIT();
  179.     addchars((BYTE *) s, strlen(s));
  180.     addchars(&c, 1);
  181.     return 0;
  182.     }
  183.  
  184. /* ---------------------------------------------------------------  */
  185. /* USED INTERNALLY - pops up an error window and returns FALSE      */
  186. /* ---------------------------------------------------------------  */
  187. int fail(BYTE *s)
  188.     {
  189.     MessageBox(NULL,s,"ERROR",MB_OK);
  190.     return FALSE;
  191.     }
  192.  
  193. /* ---------------------------------------------------------------  */
  194. /* pops up a message window                                         */
  195. /* ---------------------------------------------------------------  */
  196. BOOL winio_warn(BOOL confirm, const BYTE *fmt, ...)
  197.     {
  198.     BYTE s[256];
  199.     va_list marker;
  200.  
  201.     va_start(marker, fmt);
  202.     vsprintf(s, fmt, marker);
  203.     va_end(marker);
  204.     
  205.     return (MessageBox(NULL, s, winio_title, 
  206.         confirm? MB_OKCANCEL : MB_OK) == IDOK);
  207.     }
  208.  
  209. /* ---------------------------------------------------------------  */
  210. /* The application must call this function before using any of the  */
  211. /* covered stdio type calls. We need the parameter info in order    */
  212. /* to create the window. The function allocates the buffer and      */
  213. /* creates the window. It returns TRUE or FALSE.                    */
  214. /* ---------------------------------------------------------------  */
  215. int winio_init(HANDLE hInstance, HANDLE hPrevInstance,
  216.             int nCmdShow, unsigned wBufSize)
  217.     {
  218.     if (tWinioVisible)
  219.         return FALSE;
  220.     
  221.     if (! initialize_buffers(wBufSize))
  222.         return FALSE;
  223.  
  224.     initialize_state();
  225.     
  226.     if (! initialize_window(hInstance, hPrevInstance, nCmdShow))
  227.         return FALSE;
  228.     
  229.     tWinioVisible = TRUE;
  230.     
  231.     atexit(winio_end);  /* hook into exit chain */
  232.  
  233.     winio_yield();
  234.     return TRUE;
  235.     }
  236.  
  237. /* ---------------------------------------------------------------  */
  238. /* Clear the contents of the buffer.                                */
  239. /* ---------------------------------------------------------------  */
  240. void winio_clear(void)
  241.     {
  242.     _fmemset(fpBuffer,0,(int) bufsize - 1);
  243.     fpCurrLine = fpTopOfWin = fpBuffer;
  244.     *fpBuffer = '\0';
  245.     xCurrPos = 0;
  246.     yCurrLine = 0;
  247.     yTopOfWin = 0;
  248.     xLeftOfWin = 0;
  249.     bufused = 0;
  250.  
  251.     if (tWinioVisible)
  252.         {
  253.         SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
  254.         SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
  255.         }
  256.     }
  257.  
  258. /* ---------------------------------------------------------------  */
  259. /* Return the window handle of the underlying Windows object.       */
  260. /* Can be used by an application to customize the WINIO window      */
  261. /* ---------------------------------------------------------------  */
  262. HWND winio_hwnd(void)
  263.     {
  264.     return hwnd;
  265.     }
  266.  
  267. /* ---------------------------------------------------------------  */
  268. /* This function is called by winio_init(). It initializes a number */
  269. /* of global variables, including the WM_ handler table.            */
  270. /* ---------------------------------------------------------------  */
  271. void initialize_state()
  272.     {
  273.     winio_clear();
  274.     destroy_func = 0;
  275.     
  276.     /* set up our message handlers */
  277.     wmhandler_init();
  278.     wmhandler_set(WM_PAINT,       winio_wmpaint);
  279.     wmhandler_set(WM_SIZE,        winio_wmsize);
  280.     wmhandler_set(WM_DESTROY,     winio_wmdestroy);
  281.     wmhandler_set(WM_CHAR,        winio_wmchar);
  282.     wmhandler_set(WM_HSCROLL,     winio_wmhscroll);
  283.     wmhandler_set(WM_VSCROLL,     winio_wmvscroll);
  284.     wmhandler_set(WM_SETFOCUS,    winio_wmsetfocus);
  285.     wmhandler_set(WM_KILLFOCUS,   winio_wmkillfocus);
  286.     wmhandler_set(WM_KEYDOWN,     winio_wmkeydown);
  287.     }
  288.  
  289. /* ---------------------------------------------------------------  */
  290. /* This function is called by winio_init(). It initializes our      */
  291. /* Windows class, and some global variables                         */
  292. /* ---------------------------------------------------------------  */
  293. int initialize_window(HANDLE hInst, HANDLE hPrev, int nCmdShow)
  294.     {
  295.     static RECT start;
  296.     int cx, cy, inc;
  297.  
  298.     cx = GetSystemMetrics(SM_CXSCREEN);
  299.     cy = GetSystemMetrics(SM_CYSCREEN);
  300.     inc = GetSystemMetrics(SM_CYCAPTION);
  301.     cxScroll = GetSystemMetrics(SM_CXVSCROLL);
  302.     cyScroll = GetSystemMetrics(SM_CYHSCROLL);
  303.  
  304.     if (hPrev)
  305.         {
  306.         // note: other WINIO apps are NOT other instances!
  307.         GetInstanceData(hPrev, (NPSTR) &start, sizeof(RECT));
  308.         start.top += inc;
  309.         start.left += inc;
  310.         if (start.top > (cy >> 2))
  311.             start.top = cy >> 3;
  312.         if (start.left > (cx >> 2))
  313.             start.left = cx >> 3;
  314.         }
  315.     else
  316.         {
  317.         if (! initialize_class(hInst)) 
  318.             return fail("Could not create class");
  319.  
  320.         start.left = cx >> 3;
  321.         start.right = 3 * (cx >> 2);
  322.         start.top = cy >> 3;
  323.         start.bottom = 3 * (cy >> 2);
  324.         }
  325.         
  326.     hwnd = CreateWindow(winio_class, winio_title,
  327.         WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
  328.         start.left, start.top, start.right, start.bottom,
  329.         NULL, NULL, hInst, NULL);
  330.     if (! hwnd)
  331.         return fail("Could not create window");
  332.  
  333.     set_font();
  334.     
  335.     ShowWindow(hwnd, nCmdShow);
  336.     UpdateWindow(hwnd);
  337.  
  338.     return TRUE;
  339.     }
  340.  
  341. /* -----------------------------------------------------------------------  */
  342. /* Initializes Window Class                                                 */
  343. /* -----------------------------------------------------------------------  */
  344. int initialize_class(HANDLE hInst)
  345.     {
  346.     WNDCLASS  wc;
  347.  
  348.     wc.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;
  349.     wc.lpfnWndProc = WndProc;
  350.     wc.cbClsExtra = 0;
  351.     wc.cbWndExtra = 0;
  352.     wc.hInstance = hInst;
  353.     wc.hIcon = LoadIcon(hInst, winio_icon);
  354.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  355.     wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
  356.     wc.lpszMenuName = NULL;
  357.     wc.lpszClassName = winio_class;
  358.  
  359.     return RegisterClass(&wc);
  360.     }
  361.     
  362. /* -----------------------------------------------------------------------  */
  363. /* Uses GlobalAlloc() to allocate the display and keyboard buffers          */
  364. /* -----------------------------------------------------------------------  */
  365. int initialize_buffers(unsigned wBufSize)
  366.     {
  367.     if (wBufSize)
  368.         bufsize = max(wBufSize, 1024);
  369.  
  370.     if (! (hBuffer = GlobalAlloc(GMEM_MOVEABLE, bufsize)))
  371.         return fail("Could not allocate\nconsole I/O buffer");
  372.     
  373.     fpBuffer = GlobalLock(hBuffer); // keep locked; assume protected mode
  374.     
  375.     if (! (hKeyboard = GlobalAlloc(GMEM_MOVEABLE, kbsize)))
  376.         return fail("Could not allocate\ntype ahead buffer");
  377.         
  378.     fpKeyboard = GlobalLock(hKeyboard);
  379.  
  380.     *fpBuffer = '\0';
  381.     fpBuffer++;
  382.  
  383.     return TRUE;
  384.     }
  385.  
  386. /* -----------------------------------------------------------------------  */
  387. /* Undoes the work of the above. Allows an application to close the window  */
  388. /* Terminates the prog.                                                     */
  389. /* -----------------------------------------------------------------------  */
  390. void winio_end()
  391.     {
  392.     while (tWinioVisible)
  393.         winio_yield();
  394.     }
  395.  
  396. /* -------------------------------------------------------------------  */
  397. /* Closes the window by sending it a WM_DESTROY message. Note that it   */
  398. /* does not disable the _onclose defined function. So the user program  */
  399. /* handler will be triggered. Does NOT cause the app. to terminate.     */
  400. /* -------------------------------------------------------------------  */
  401. void winio_close()
  402.     {
  403.     tTerminate = FALSE;
  404.     DestroyWindow(hwnd);
  405.     tTerminate = TRUE;
  406.     }
  407.     
  408. /* -------------------------------------------------------------------  */
  409. /* processes any outstanding events waiting. These may be characters    */
  410. /* typed at the keyboard, WM_PAINT messages, etc. It is called          */
  411. /* internally but should also be used liberally by the application      */
  412. /* within loops.                                                        */
  413. /* -------------------------------------------------------------------  */
  414. void winio_yield()
  415.     {
  416.     MSG msg;
  417.     while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
  418.         {
  419.         TranslateMessage(&msg);
  420.         DispatchMessage(&msg);
  421.         }
  422.     }
  423.  
  424. /* -------------------------------------------------------------------  */
  425. /* Let the application install an exit routine, called back from        */
  426. /* winio_wmdestroy(). Deinstall by winio_onclose(NULL)                  */
  427. /* -------------------------------------------------------------------  */
  428. void winio_onclose(DESTROY_FUNC exitfunc)
  429.     {
  430.     destroy_func = exitfunc;
  431.     }
  432.  
  433. /* -------------------------------------------------------------------  */
  434. /* This function allows the font of the window to be modified, and may  */
  435. /* be used BEFORE winio_init. Currently, only SYSTEM_, ANSI_, and       */
  436. /* OEM_FIXED_FONTs are supported.                                       */
  437. /* -------------------------------------------------------------------  */
  438. BOOL winio_setfont(WORD wFont)
  439.     {
  440.     if ((wFont != SYSTEM_FIXED_FONT) &&
  441.         (wFont != ANSI_FIXED_FONT) &&
  442.         (wFont != OEM_FIXED_FONT) )
  443.         return FALSE;
  444.     curr_font = wFont;
  445.     if (tWinioVisible)
  446.         {
  447.         set_font();
  448.         if (tPaint)
  449.             InvalidateRect(hwnd, NULL, TRUE);
  450.         }
  451.     return TRUE;
  452.     }
  453.  
  454. /* -------------------------------------------------------------------  */
  455. /* This function allows the title of the window to be modified, and may */
  456. /* be used BEFORE winio_init.                                           */
  457. /* -------------------------------------------------------------------  */
  458. void winio_settitle(BYTE *pchTitle)
  459.     {
  460.     strncpy(winio_title, pchTitle, 127);
  461.     winio_title[127] = '\0';
  462.     if (tWinioVisible)
  463.         SetWindowText(hwnd, winio_title);
  464.     }
  465.  
  466. /* -------------------------------------------------------------------  */
  467. /* This function allows the caller to specifiy immediate or deferred    */
  468. /* screen updates. The call may not be issued before winio_init().      */
  469. /* -------------------------------------------------------------------  */
  470. BOOL winio_setpaint(BOOL on)
  471.     {
  472.     BOOL ret = tPaint;
  473.     
  474.     CHECK_INIT();
  475.     if (tPaint = on)
  476.         InvalidateRect(hwnd, NULL, TRUE);
  477.     return ret;
  478.     }
  479.  
  480. /* ---------------------------------------------------------------  */
  481. /* Our WM_PAINT handler. It sends the currrent 'in view' piece of   */
  482. /* the buffer to the window. Note that an embedded NULL character   */
  483. /* signifies an end of line, not '\n'.                              */
  484. /* ---------------------------------------------------------------  */
  485. long winio_wmpaint(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  486.     {
  487.     HDC hdc;
  488.     PAINTSTRUCT ps;
  489.     BYTE far *pchSOL = fpTopOfWin;
  490.     BYTE far *pchEOL;
  491.     int i, j, xStart;
  492.     int xLeft, xRight, yTop, yBottom;
  493.  
  494.     hdc = BeginPaint(hwnd, &ps);
  495.  
  496.     xLeft = (ps.rcPaint.left / cxChar) + xLeftOfWin;
  497.     xRight = (ps.rcPaint.right / cxChar) + xLeftOfWin;
  498.     yTop = ps.rcPaint.top / cyChar;
  499.     yBottom = ps.rcPaint.bottom / cyChar;
  500.     SelectObject(hdc, GetStockObject(curr_font));
  501.  
  502.     for (i = 0; i < yTop; i++)      // lines above repaint region
  503.         {
  504.         while (*pchSOL)
  505.             pchSOL++;
  506.         pchSOL++;
  507.         }
  508.  
  509.     if (i <= yCurrLine) // something needs repainting..
  510.         {
  511.         for (i = yTop; i <= yBottom; i++)   // lines in repaint region
  512.             {
  513.             for (j = 0; (j < xLeft) && (*pchSOL); j++, pchSOL++)
  514.                 ; // Scroll right
  515.             pchEOL = pchSOL;
  516.             xStart = j - xLeftOfWin;
  517.             for (j = 0; (*pchEOL) ; j++, pchEOL++) ; // end of line
  518.             TextOut(hdc, cxChar * xStart, cyChar * i, pchSOL,
  519.                     min(j, xRight - xLeft + 2));
  520.             if ((unsigned)(pchEOL - fpBuffer) >= bufused)
  521.                 break;
  522.             pchSOL = ++pchEOL;  
  523.             }
  524.         }
  525.     
  526.     EndPaint(hwnd, &ps);
  527.     adjust_caret();
  528.     return 0;
  529.     }
  530.  
  531. /* ---------------------------------------------------------------  */
  532. /* Our WM_SIZE handler. It updates the internal record of our       */
  533. /* window size, minus the scroll bars, and recalcs the scroll bar   */
  534. /* ranges.                                                          */
  535. /* ---------------------------------------------------------------  */
  536. long winio_wmsize(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  537.     {
  538.     cxWidth = LOWORD(lParam);
  539.     cyHeight = HIWORD(lParam);
  540.  
  541.     xWinWidth   = (cxWidth - cxScroll ) / cxChar;
  542.     yWinHeight  = (cyHeight - cyScroll ) / cyChar;
  543.  
  544.     cScrollLR[SB_PAGEUP]    = -xWinWidth / 2;
  545.     cScrollLR[SB_PAGEDOWN]  = +xWinWidth / 2;
  546.     cScrollUD[SB_PAGEUP]    = -yWinHeight + 1;
  547.     cScrollUD[SB_PAGEDOWN]  = +yWinHeight - 1;
  548.     
  549.     SetScrollRange(hwnd, SB_HORZ, 1, MAX_X, FALSE);
  550.     SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
  551.  
  552.     SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
  553.     SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
  554.     
  555.     return 0;
  556.     }
  557.  
  558. /* ---------------------------------------------------------------  */
  559. /* Our WM_DESTROY handler. It frees up storage associated with the  */
  560. /* window, and resets its state to uninitialized.                   */
  561. /* ---------------------------------------------------------------  */
  562. long winio_wmdestroy(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  563.     {
  564.     if (destroy_func)
  565.         (*destroy_func)();
  566.     GlobalUnlock(hBuffer);
  567.     GlobalUnlock(hKeyboard);
  568.     GlobalFree(hBuffer);
  569.     GlobalFree(hKeyboard);
  570.     tWinioVisible = FALSE;
  571.     if (tTerminate) exit(0);
  572.     return 0;
  573.     }
  574.  
  575. /* --------------------------------------------------------------- */
  576. /* Our WM_BYTE handler. It adds the BYTE to the internal kb buffer */
  577. /* if there is room otherwise it queues a BEEP                     */
  578. /* --------------------------------------------------------------- */
  579. long winio_wmchar(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  580.     {
  581.     unsigned BYTE far *lpchKeybd = fpKeyboard;
  582.     unsigned pchSave = pchKbIn;
  583.     
  584.     pchKbIn++;
  585.     if (pchKbIn == TYPE_AHEAD)
  586.         pchKbIn = 0;
  587.     if (pchKbIn == pchKbOut)
  588.         {
  589.         MessageBeep(0);
  590.         pchKbIn = pchSave;
  591.         }
  592.     else
  593.         *(lpchKeybd + pchSave) = LOBYTE(wParam);
  594.  
  595.     return 0;
  596.     }
  597.  
  598. /* ---------------------------------------------------------------  */
  599. /* Our WM_KEYDOWN handler. This handles what would be called        */
  600. /* function keys in the DOS world. In this case the function keys   */
  601. /* operate as scroll bar controls, so we generate messages to the   */
  602. /* scroll message handlers below.                                   */
  603. /* ---------------------------------------------------------------  */
  604. long winio_wmkeydown(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  605.     {
  606.     int hSB, vSB;
  607.     
  608.     if ((wParam < VK_PRIOR) || (wParam > VK_DOWN))
  609.         return 0;
  610.     
  611.     hSB = VKtoSB[wParam - VK_PRIOR].hSB;
  612.     vSB = VKtoSB[wParam - VK_PRIOR].vSB;
  613.     if (hSB != DO_NOTHING)
  614.         SendMessage(hwnd, WM_HSCROLL, hSB, 0L);
  615.     if (vSB != DO_NOTHING)
  616.         SendMessage(hwnd, WM_VSCROLL, vSB, 0L);
  617.     return 0;
  618.     }
  619.  
  620. /* --------------------------------------------------------------- */
  621. /* Our WM_HSCROLL handler. It adjusts what part of the buffer      */
  622. /* is visible. It operates as left/right arrow keys.               */
  623. /* --------------------------------------------------------------- */
  624. long winio_wmhscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  625.     {
  626.     int cxSave = xLeftOfWin,
  627.         xInc = cScrollLR[wParam];
  628.     
  629.     if (xInc == DO_NOTHING)
  630.         return 0;
  631.     else if (xInc == USE_PARAM)
  632.         xLeftOfWin = LOWORD(lParam) - 1;
  633.     else
  634.         xLeftOfWin += xInc;
  635.     
  636.     if ((xLeftOfWin = max(0, min(MAX_X - 1, xLeftOfWin))) == cxSave)
  637.         return 0;
  638.  
  639.     ScrollWindow(hwnd, (cxSave - xLeftOfWin) * cxChar, 0, NULL, NULL);
  640.     SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
  641.     UpdateWindow(hwnd);
  642.  
  643.     return 0;
  644.     }
  645.  
  646. /* --------------------------------------------------------------- */
  647. /* Our WM_VSCROLL handler. It adjusts what part of the buffer      */
  648. /* is visible. It operates as page and line up/down keys.          */
  649. /* --------------------------------------------------------------- */
  650. long winio_wmvscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  651.     {
  652.     int cySave = yTopOfWin,
  653.         yInc = cScrollUD[wParam],
  654.         i;
  655.     
  656.     if (yInc == DO_NOTHING)
  657.         return 0;
  658.     else if (yInc == USE_PARAM)
  659.         yTopOfWin = LOWORD(lParam) - 1;
  660.     else
  661.         yTopOfWin += yInc;
  662.  
  663.     if ((yTopOfWin = max(0, min(yCurrLine, yTopOfWin))) == cySave)
  664.         return 0;
  665.  
  666.     if (yTopOfWin > cySave)
  667.         for (i = cySave; i < yTopOfWin; i++)
  668.             fpTopOfWin = nextline(fpTopOfWin);
  669.     else
  670.         for (i = cySave; i > yTopOfWin; i--)
  671.             fpTopOfWin = prevline(fpTopOfWin);
  672.         
  673.     ScrollWindow(hwnd, 0, (cySave - yTopOfWin) * cyChar, NULL, NULL);
  674.     SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
  675.     UpdateWindow(hwnd);
  676.  
  677.     return 0;
  678.     }
  679.  
  680. /* ---------------------------------------------------------------  */
  681. /* Our WM_SETFOCUS handler. It sets up the text caret.              */
  682. /* ---------------------------------------------------------------  */
  683. long winio_wmsetfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  684.     {
  685.     CreateCaret(hwnd, NULL, CARET_WIDTH, cyChar);
  686.     
  687.     if ((tCaret = winio_caret_visible()))
  688.         {
  689.         SetCaretPos((xCurrPos - xLeftOfWin) * cxChar,
  690.                     (yCurrLine - yTopOfWin) * cyChar);
  691.         ShowCaret(hwnd);
  692.         }
  693.  
  694.     return 0;
  695.     }
  696.  
  697. /* ---------------------------------------------------------------  */
  698. /* Our WM_KILLFOCUS handler. It destroys the text caret.            */
  699. /* ---------------------------------------------------------------  */
  700. long winio_wmkillfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
  701.     {
  702.     if (tCaret)
  703.         {
  704.         HideCaret(hwnd);
  705.         tCaret = FALSE;
  706.         }
  707.     DestroyCaret();
  708.     return 0;
  709.     }
  710.  
  711. void set_font(void)
  712.     {
  713.     HDC hdc;
  714.     TEXTMETRIC tm;
  715.         
  716.     hdc = GetDC(hwnd);
  717.     SelectObject(hdc, GetStockObject(curr_font));
  718.     GetTextMetrics(hdc,&tm);
  719.     ReleaseDC(hwnd,hdc);
  720.     cxChar = tm.tmAveCharWidth;
  721.     cyChar = tm.tmHeight+tm.tmExternalLeading;
  722.     xWinWidth   = (cxWidth - cxScroll ) / cxChar;
  723.     yWinHeight  = (cyHeight - cyScroll ) / cyChar;
  724.     }
  725.  
  726. /* ---------------------------------------------------------------  */
  727. /* Adjusts the position of the caret, and shows or hides it, as     */
  728. /* appropriate.                                                     */
  729. /* ---------------------------------------------------------------  */
  730. void adjust_caret()
  731.     {
  732.     int t = winio_caret_visible();
  733.  
  734.     if (t)
  735.         SetCaretPos((xCurrPos - xLeftOfWin) * cxChar,
  736.                     (yCurrLine - yTopOfWin) * cyChar);
  737.     if (t && (! tCaret))
  738.         ShowCaret(hwnd);
  739.     if ((! t) && tCaret)
  740.         HideCaret(hwnd);
  741.     tCaret = t;
  742.     }
  743.  
  744. /* ---------------------------------------------------------------  */
  745. /* Computes, on the basis of what has just been updated, what area  */
  746. /* of the window needs to be repainted.                             */
  747. /* ---------------------------------------------------------------  */
  748. void compute_repaint(void)
  749.     {
  750.     RECT rc;
  751.     static int xCP = 0, yCL = 0;
  752.     int tWholeWin = FALSE;
  753.     
  754.     if (yCurrLine > (yTopOfWin + yWinHeight))
  755.         {
  756.         yTopOfWin = 0;
  757.         fpTopOfWin = fpBuffer;
  758.         while (yTopOfWin < (yCurrLine - ((yWinHeight + 1) / 2)))
  759.             {
  760.             fpTopOfWin = nextline(fpTopOfWin);
  761.             yTopOfWin++;
  762.             }
  763.         tWholeWin = TRUE;
  764.         }
  765.  
  766.     if ((xCurrPos < xLeftOfWin) || (xCurrPos > (xLeftOfWin + xWinWidth)))
  767.         {
  768.         xLeftOfWin = 0;
  769.         while (xLeftOfWin < (xCurrPos - ((xWinWidth + 1) / 2)))
  770.             xLeftOfWin++;
  771.         tWholeWin = TRUE;
  772.         }
  773.  
  774.     if (tWholeWin)
  775.         InvalidateRect(hwnd, NULL, TRUE);
  776.     else
  777.         {
  778.         rc.left = ((yCL == yCurrLine) ?
  779.             (min(xCP, xCurrPos) - xLeftOfWin) * cxChar : 0);
  780.         rc.top = (yCL - yTopOfWin) * cyChar;
  781.         rc.right = (xWinWidth + 1) * cxChar;
  782.         rc.bottom = (yCurrLine - yTopOfWin + 1) * cyChar;
  783.         InvalidateRect(hwnd, &rc, TRUE);
  784.         }
  785.     
  786.     yCL = yCurrLine;
  787.     xCP = xCurrPos;
  788.     }
  789.  
  790. /* ---------------------------------------------------------------  */
  791. /* Adds the supplied cch-long string to the display buffer, and     */
  792. /* ensures any changed part of the window is repainted.             */
  793. /* ---------------------------------------------------------------  */
  794. void addchars(BYTE *pch, unsigned cch)
  795.     {
  796.     int ycSave = yCurrLine;
  797.     int ytSave = yTopOfWin;
  798.     int xSave = xLeftOfWin;
  799.  
  800.     make_avail(cch);
  801.  
  802.     append2buffer(pch, cch);
  803.  
  804.     if (ycSave != yCurrLine)
  805.         SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
  806.  
  807.     if (! tPaint)
  808.         return;
  809.     
  810.     compute_repaint();
  811.  
  812.     cScrollUD[SB_TOP]       = -yCurrLine;
  813.     cScrollUD[SB_BOTTOM]    = yCurrLine;
  814.     if (ytSave != yTopOfWin)
  815.         SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);       
  816.  
  817.     if (xSave != xLeftOfWin)
  818.         SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
  819.  
  820.     winio_yield();
  821.     }
  822.  
  823. /* ---------------------------------------------------------------  */
  824. /* Add chars onto the display buffer, wrapping at end of line,      */
  825. /* expanding tabs, etc.                                             */
  826. /* ---------------------------------------------------------------  */
  827. void append2buffer(BYTE *pch, unsigned cch)
  828.     {
  829.     unsigned i;
  830.     
  831.     for (i = 0; i < cch; i++, pch++)
  832.         {
  833.         switch (*pch)
  834.             {
  835.             case '\n' :
  836.                 *pch = '\0';
  837.                 *(fpBuffer + bufused) = '\0';
  838.                 bufused++;
  839.                 fpCurrLine = fpBuffer + bufused;
  840.                 yCurrLine++;
  841.                 xCurrPos = 0;
  842.                 bufSOI = bufused;
  843.                 break;
  844.             case '\t' :
  845.                 do  {
  846.                     *(fpBuffer + bufused) = ' ';
  847.                     bufused++;
  848.                     xCurrPos++;
  849.                     } while ((xCurrPos % TABSIZE) != 0);
  850.                 break;
  851.             case EOF :
  852.                 break;
  853.             case '\b' :
  854.                 if (bufused > bufSOI)
  855.                     {
  856.                     bufused--;
  857.                     xCurrPos--;
  858.                     }
  859.                 break;
  860.             case 0x1b :
  861.                 while (bufused > bufSOI)
  862.                     {
  863.                     bufused--;
  864.                     xCurrPos--;
  865.                     }
  866.                 break;
  867.             case 0x07 :
  868.                 MessageBeep(0);
  869.                 break;
  870.             default :
  871.                 if (*pch > 0x1a)
  872.                     {
  873.                     if (xCurrPos >= MAX_X)
  874.                         {
  875.                         *(fpBuffer + bufused) = '\0';
  876.                         bufused++;
  877.                         xCurrPos = 0;
  878.                         yCurrLine++;
  879.                         fpCurrLine = fpBuffer + bufused;
  880.                         }
  881.                     xCurrPos++;
  882.                     *(fpBuffer + bufused) = *pch;
  883.                     bufused++;
  884.                     }
  885.             }
  886.         }
  887.     
  888.     *(fpBuffer + bufused) = '\0'; // '\0' terminator after end of buffer
  889.     }
  890.  
  891. /* ---------------------------------------------------------------  */
  892. /* If we have run out of room in the display buffer, drop whole     */
  893. /* lines, and move the remaining buffer up.                         */
  894. /* ---------------------------------------------------------------  */
  895. void make_avail(unsigned cch)
  896.     {
  897.     unsigned cDiscard = 0;
  898.     BYTE far *fpTmp;
  899.     unsigned i;
  900.  
  901.     if ((unsigned long)(bufused + cch + TABSIZE) < bufsize)
  902.         return;
  903.  
  904.     fpTmp = fpBuffer;
  905.     cDiscard = ((max(MIN_DISCARD, cch + 1) + MIN_DISCARD - 1)
  906.         / MIN_DISCARD)      // this gives a whole number of
  907.         * MIN_DISCARD;      // our allocation units.
  908.     fpTmp += (LONG) cDiscard;
  909.     fpTmp = nextline(fpTmp);
  910.     cDiscard = fpTmp - fpBuffer; 
  911.     _fmemcpy(fpBuffer, fpTmp, bufused - cDiscard + 1);
  912.     bufused -= cDiscard;
  913.     if ((int) bufSOI != -1) bufSOI -= cDiscard;
  914.     fpTmp = fpBuffer + (LONG) bufused;
  915.     for (i = 0; i < cDiscard; i++) *fpTmp++ = '\0';
  916.     fpCurrLine = fpBuffer;
  917.     xCurrPos = yCurrLine = 0;
  918.     for (i = 0; i < bufused; i++)
  919.         {
  920.         if (*fpCurrLine)
  921.             xCurrPos++;
  922.         else
  923.             {
  924.             xCurrPos = 0;
  925.             yCurrLine++;
  926.             }
  927.         fpCurrLine++;
  928.         }
  929.     xLeftOfWin = yTopOfWin = -9999;
  930.     
  931.     InvalidateRect(hwnd, NULL, TRUE);
  932.     }
  933.  
  934.  
  935. /* -------------------------------------------------------------------  */
  936. /* These two routines find the beginning of the next, and previous      */
  937. /* lines relative to their input pointer                                */
  938. /* -------------------------------------------------------------------  */
  939.  
  940. BYTE far *nextline(BYTE far *p) { while (*p) p++; return ++p; }
  941. BYTE far *prevline(BYTE far *p) { p--; do p--; while (*p); return ++p; }
  942.  
  943. /* -------------------------------------------------------------------  */
  944. /* Waits for a character to appear in the keyboard buffer, yielding     */
  945. /* while nothing is available. Then inserts it into the buffer.         */
  946. /* -------------------------------------------------------------------  */
  947. int chInput(void)
  948.     {
  949.     BYTE far *lpchKeyBd;
  950.     BYTE c;
  951.     
  952.     CHECK_INIT();
  953.     while (pchKbIn == pchKbOut)
  954.         winio_yield();
  955.         
  956.     lpchKeyBd = fpKeyboard;
  957.     c = *(lpchKeyBd + pchKbOut);
  958.  
  959.     pchKbOut++;
  960.     if (pchKbOut == TYPE_AHEAD)
  961.         pchKbOut = 0;
  962.     
  963.     // Do CR/LF and EOF translation
  964.     return (c == 0x1a) ? EOF : (c == '\r') ? '\n' : c;
  965.     }
  966.  
  967.